﻿
using KnBinarySerializer.Field;
using KnBinarySerializer.Ver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace KnBinarySerializer
{
    [FormatterVer(FormatVer.Ver1)]
    internal class ClassFormatter : IFormatter
    {        
        public Type GetHandleType()
        {
            return typeof(object);
        }

        public byte GetKey()
        {
            return (byte)0;
        }
        public bool IsFixLen()
        {
            return false;
        }

        private static Dictionary<string, IClassField> getFields(Type type)
        {
            Dictionary<string, IClassField> result = new Dictionary<string, IClassField>();
            PropertyInfo[] propertyInfos = type.GetProperties();
            foreach (PropertyInfo property in propertyInfos)
            {
                if (property.GetCustomAttributes(typeof(NonSerializedAttribute), true).Length > 0)
                    continue;
                MethodInfo method = property.GetGetMethod();
                if (method != null && method.Name == "get_Item")
                    continue;
                result.Add(property.Name, new ClassProperty(property));
            }

            FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public);
            foreach (FieldInfo field in fields)
            {
                if (field.GetCustomAttributes(typeof(NonSerializedAttribute), true).Length > 0)
                    continue;               
                result.Add(field.Name, new ClassField(field));
            }
            return result;
        }
        public int WriteBytes(BinarySerializeManager stream, object obj)
        {
            int len = 0;
            if (obj != null)
            {
                Type type = obj.GetType();
                Dictionary<string, IClassField> fields = getFields(type);
                foreach(var field in fields)
                {
                    IClassField property = field.Value;
                    object objVal = property.GetValue(obj, null);
                    if (objVal == null)
                        continue;
                    string proName = field.Key;
                    byte[] propertyBytes = Encoding.UTF8.GetBytes(proName);
                    if (propertyBytes.Length > byte.MaxValue)
                    {
                        throw new Exception($"属性名：{proName}超长");
                    }
                    byte lenBytes = (byte)propertyBytes.Length;
                    stream.Write(lenBytes);
                    stream.Write(propertyBytes);
                    len += propertyBytes.Length + 1;
                    len += stream.SerializeObject(objVal);
                }
            }
            return len;
        }

        public object ReadBytes(BinarySerializeManager content, int maxLen, Type type)
        {
            if (maxLen == 0)
                return null;
            long start = content.GetPosition();
            object obj = ClassSerialContext.CreateObject(type);
            int index = 0;
            Dictionary<string, IClassField> fields = getFields(type);
            while (content.GetPosition() - start < maxLen)
            {
                //读取属性名称
                byte len = content.ReadByte();
                index++;
                byte[] nameBytes = content.ReadBytes(len);                
                string name = Encoding.UTF8.GetString(nameBytes);
                if (!fields.ContainsKey(name))
                {
                    byte misKey = content.ReadByte();
                    IFormatter formatter = content.GetFormatter(misKey);
                    int misLen = 0;
                    if (formatter.IsFixLen())
                    {
                        misLen = content.GetBytesLen(formatter.GetHandleType());
                        if (misLen == -1)
                            throw new Exception($"固定长度属性无法忽略，请定义{type.Name}.{name}[{formatter.GetHandleType().Name}]属性");
                    }
                    else
                    {
                        misLen = content.ReadInt();
                    }
                    content.Skip(misLen);
                    continue;
                }
                IClassField property = fields[name];
                if (property.CanWrite)
                {
                    object objVal = content.DeSerializeObject(property.PropertyType);
                    property.SetValue(obj, objVal, null);
                }
                else
                {
                    //不能写的，视为子进行子项添加
                    object objVal = property.GetValue(obj, null);
                    if (objVal == null)
                        throw new Exception("不能写的属性，需要类自行初始化才能进行反序列化");
                    content.DeSerializeToObject(objVal);
                }
            }
            return obj;
        }

    }

    
}
